package coreservice

import (
	"core/config"
	"core/corestruct"
	"core/coreutil"
	"gorm.io/gorm"
	"reflect"
	"strconv"
	"time"
)

const DetailCachePrev = "detail"

type baseService struct {
}

/**
*获取http上下文
 */
func (p *baseService) GetCtx(db *gorm.DB) *corestruct.HttpContext {
	var v = db.Statement.Context.Value(corestruct.KEY_HttpContext)
	return v.(*corestruct.HttpContext)
}

/**
*获取插入Map
 */
func (p *baseService) GetInsertMapByDto(src, dst interface{}, ctx *corestruct.HttpContext) map[string]interface{} {

	var r = make(map[string]interface{})
	dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// dst必须结构体指针类型
	if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
		panic("dst type should be a struct pointer")
	}

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		panic("src type should be a struct or a struct pointer")
	}

	// 取具体内容
	dstType, dstValue = dstType.Elem(), dstValue.Elem()

	// 属性个数
	NumField := dstType.NumField()

	for i := 0; i < NumField; i++ {
		if !dstValue.Field(i).CanSet() {
			continue
		}
		// 属性
		property := dstType.Field(i)
		r[property.Name] = dstValue.Field(i).Interface()
		switch property.Name {
		case "CrtDt", "EdtDt":
			r[property.Name] = corestruct.MyTime(time.Now())
			continue
		case "CrtUid", "EdtUid":
			r[property.Name] = ctx.Session.CrtUid
			continue
		case "CrtDid":
			r[property.Name] = ctx.Session.HtdeptId
			continue
		case "Gid":
			r[property.Name] = coreutil.IdUtil.UuidStr()
			continue
		default:
			break
		}

		// 待填充属性值
		propertyValue := srcValue.FieldByName(property.Name)

		// 无效，说明src没有这个属性 || 属性同名但类型不同
		if !propertyValue.IsValid() || property.Type != propertyValue.Type() {

		} else {
			if dstValue.Field(i).CanSet() {
				//不考虑trim
				//if propertyValue.Type().Kind() == reflect.String {
				//	//trim
				//	r[property.Name] = strings.TrimSpace(propertyValue.String())
				//} else {
				//
				//}
				r[property.Name] = propertyValue.Interface()
			}
		}
	}
	//id
	var Id, ok = r["Id"]
	if ok {
		if Id.(int64) == 0 {
			r["Id"] = coreutil.IdUtil.GetId()
		}
	} else {
		r["Id"] = coreutil.IdUtil.GetId()
	}

	return r
}

/**
*获取插入Map
 */
func (p *baseService) GetInsertMap(src interface{}, ctx *corestruct.HttpContext) map[string]interface{} {

	var r = make(map[string]interface{})
	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		panic("src type should be a struct or a struct pointer")
	}

	// 属性个数
	NumField := srcType.NumField()

	for i := 0; i < NumField; i++ {
		if !srcValue.Field(i).CanSet() {
			continue
		}

		// 属性
		property := srcType.Field(i)

		r[property.Name] = srcValue.Field(i).Interface()
		switch property.Name {
		case "CrtDt", "EdtDt":
			r[property.Name] = corestruct.MyTime(time.Now())
			continue
		case "CrtUid", "EdtUid":
			r[property.Name] = ctx.Session.CrtUid
			continue
		case "CrtDid":
			r[property.Name] = ctx.Session.HtdeptId
			continue
		case "Gid":
			r[property.Name] = coreutil.IdUtil.UuidStr()
			continue
		default:
			break
		}

		// 待填充属性值
		propertyValue := srcValue.FieldByName(property.Name)
		if srcValue.Field(i).CanSet() {
			//不考虑trim
			//if propertyValue.Type().Kind() == reflect.String {
			//	//trim
			//	r[property.Name] = strings.TrimSpace(propertyValue.String())
			//} else {
			//
			//}
			r[property.Name] = propertyValue.Interface()
		}
	}
	//id
	var Id, ok = r["Id"]
	if ok {
		if Id.(int64) == 0 {
			r["Id"] = coreutil.IdUtil.GetId()
		}
	} else {
		r["Id"] = coreutil.IdUtil.GetId()
	}

	return r
}

/**
*获取更新Map
 */
func (p *baseService) GetUpdateMapByDto(src, dst interface{}, ctx *corestruct.HttpContext) map[string]interface{} {
	var r = make(map[string]interface{})
	dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// dst必须结构体指针类型
	if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
		panic("dst type should be a struct pointer")
	}

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		panic("src type should be a struct or a struct pointer")
	}

	// 取具体内容
	dstType, dstValue = dstType.Elem(), dstValue.Elem()

	// 属性个数
	NumField := dstType.NumField()

	for i := 0; i < NumField; i++ {
		// 属性
		property := dstType.Field(i)
		switch property.Name {
		case "Id", "CrtDt", "CrtUid", "CrtDid", "Gid", "Deleted":
			continue
		case "EdtDt":
			r[property.Name] = corestruct.MyTime(time.Now())
			continue
		case "EdtUid":
			r[property.Name] = ctx.Session.CrtUid
			continue
		default:

		}
		// 待填充属性值
		propertyValue := srcValue.FieldByName(property.Name)

		// 无效，说明src没有这个属性 || 属性同名但类型不同
		if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
			continue
		}

		if dstValue.Field(i).CanSet() {
			r[property.Name] = propertyValue.Interface()
		}
	}
	return r
}

/*
*插入数据
 */
func (p *baseService) Insert(dst interface{}, db *gorm.DB) *gorm.DB {
	var ctx = p.GetCtx(db)
	var mp = p.GetInsertMap(dst, ctx)
	var tx = db.Model(dst).Create(mp)
	return tx
}

/*
*通过dto插入数据
 */
func (p *baseService) InsertByDto(src, dst interface{}, db *gorm.DB) *gorm.DB {
	var ctx = p.GetCtx(db)
	var mp = p.GetInsertMapByDto(src, dst, ctx)

	var tx = db.Model(dst).Create(mp)
	return tx
}

/**
*通过dto更新数据
 */
func (p *baseService) UpdateByDto(src, dst interface{}, db *gorm.DB, omitFileds ...string) *gorm.DB {
	var Id = coreutil.StructUtil.GetFieldValue(src, "Id").(int64)

	var ctx = p.GetCtx(db)
	var mp = p.GetUpdateMapByDto(src, dst, ctx)
	if len(omitFileds) > 0 {
		for key := range mp {
			for i := 0; i < len(omitFileds); i++ {
				if omitFileds[i] == key {
					delete(mp, key)
				}
			}
		}
	}
	var tx = db.Model(dst).Updates(mp)
	if tx.Error == nil {
		p.DeleteCacheById(Id)
	}
	return tx
}

/**
*删除数据
 */
func (p *baseService) Delete(src interface{}, db *gorm.DB) *gorm.DB {
	var ctx = p.GetCtx(db)
	var Id = coreutil.StructUtil.GetFieldValue(src, "Id").(int64)

	if coreutil.StructUtil.HasField(src, "Name") {
		tx := db.Model(src).Where(src).Select("name").Scan(&ctx.LogName)
		if tx.Error != nil {
			return tx
		}
	}
	r := make(map[string]interface{})
	srcType := reflect.TypeOf(src)
	var srcValue = reflect.ValueOf(src)
	// dst必须结构体指针类型
	if srcValue.Kind() == reflect.Ptr {
		srcType = srcType.Elem()
		srcValue = srcValue.Elem()
	}

	NumField := srcType.NumField()
	for i := 0; i < NumField; i++ {
		property := srcType.Field(i)
		switch property.Name {
		case "EdtDt":
			r[property.Name] = corestruct.MyTime(time.Now())
			break
		case "EdtUid":
			r[property.Name] = ctx.Session.CrtUid
			break

		case "Deleted":
			r[property.Name] = 1
			break
		default:
			break
		}
	}
	if _, ok := r["Deleted"]; ok {

		var tx = db.Model(src).Updates(r)
		if tx.Error == nil {
			p.DeleteCacheById(Id)
		}
		return tx
	} else {
		var tx = db.Delete(src)
		if tx.Error == nil {
			p.DeleteCacheById(Id)
		}
		return tx
	}

}

/**
*根据id获取数据
 */
func (p *baseService) GetById(dst interface{}, fromCache bool, db *gorm.DB) error {
	var id = p.GetFieldValue(dst, "Id").(int64)
	var cacheId = DetailCachePrev + strconv.FormatInt(id, 10)
	if fromCache {

		err := coreutil.RedisGetStruct(cacheId, dst)
		if err == nil {
			return nil
		}
	}
	var tx = db.Find(dst)
	if tx.Error == nil {
		coreutil.RedisSetStruct(cacheId, dst, config.Config.Cache.DefautSecond)
	}
	return tx.Error
}

/**
*删除缓存
 */
func (p *baseService) DeleteCacheById(id int64) error {
	var cacheId = DetailCachePrev + strconv.FormatInt(id, 10)
	var _, err = coreutil.RedisUtil.Del(cacheId).Result()
	return err
}

/**
*根据id获取名称数据
 */
func (p *baseService) ListIdName(dst interface{}, db *gorm.DB) ([]corestruct.IdNameDto, error) {
	var r []corestruct.IdNameDto
	tx := db.Model(dst).Where(dst).
		Select("id,name").
		Order("id").
		Find(&r)
	if tx.Error != nil {
		return nil, tx.Error
	}
	return r, nil
}

/**
*根据id获取名称数据
 */
func (p *baseService) GetName(dst interface{}, db *gorm.DB) string {
	var r *corestruct.IdNameDto
	tx := db.Model(dst).Where(dst).
		Select("id,name").
		Find(&r)
	if tx.Error != nil {
		return ""
	}
	return r.Name
}

//func (p *baseService) Find(db *gorm.DB, entity interface{}) error {
//	var tx = db.Find(entity)
//	return tx.Error
//}

/*
*根据字段名称获取指端值
 */
func (p *baseService) GetFieldValue(src interface{}, fieldName string) interface{} {
	var srcValue = reflect.ValueOf(src)
	// dst必须结构体指针类型
	if srcValue.Kind() == reflect.Ptr {
		srcValue = srcValue.Elem()
	}
	var f = srcValue.FieldByName(fieldName)
	if f.IsValid() {
		return f.Interface()
	} else {
		return nil
	}
}

/*
*根据字段序号获取指端值
 */
func (p *baseService) GetFieldValueByIndex(src interface{}, i int) interface{} {
	var srcValue = reflect.ValueOf(src)
	// dst必须结构体指针类型
	if srcValue.Kind() == reflect.Ptr {
		srcValue = srcValue.Elem()
	}
	var f = srcValue.Field(i)
	if f.IsValid() {
		return f.Interface()
	} else {
		return nil
	}
}

/*
*获取Map
 */
func (p *baseService) GetStructMap(src interface{}) map[string]interface{} {
	r := make(map[string]interface{})
	srcType := reflect.TypeOf(src)
	srcValue := reflect.ValueOf(src)
	// dst必须结构体指针类型
	if srcType.Kind() == reflect.Ptr {
		srcType = srcType.Elem()
		srcValue = srcValue.Elem()
	}
	NumField := srcType.NumField()
	for i := 0; i < NumField; i++ {
		r[srcType.Field(i).Name] = srcValue.Field(i).Interface()
	}
	return r
}

func (p *baseService) SimpleCopy(src, dst interface{}) {

	dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// dst必须结构体指针类型
	if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
		panic("dst type should be a struct pointer")
	}

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		panic("src type should be a struct or a struct pointer")
	}

	// 取具体内容
	dstType, dstValue = dstType.Elem(), dstValue.Elem()

	// 属性个数
	NumField := dstType.NumField()

	for i := 0; i < NumField; i++ {
		// 属性
		property := dstType.Field(i)
		// 待填充属性值
		propertyValue := srcValue.FieldByName(property.Name)

		// 无效，说明src没有这个属性 || 属性同名但类型不同
		if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
			continue
		}

		if dstValue.Field(i).CanSet() {
			dstValue.Field(i).Set(propertyValue)
		}
	}
}

var (
	BaseService = &baseService{}
)
